﻿using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using DimensionsHelper.Common.Contracts.Metadata;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.LanguageService.Analyzer;
using DimensionsHelper.LanguageService.Protocol;
using DimensionsHelper.LanguageService.Symbols;
using DimensionsHelper.LanguageService.Syntax;
using DimensionsHelper.Services.Metadata;

namespace DimensionsHelper.LanguageService;

/// <summary>
///     存储整个代码项目的所有信息，包括源文件、符号表等。项目读取的是一个文件夹，
///     在项目载入时会读取文件夹内的所有有效文件，之后会监视文件夹内的文件变化，
///     并在添加、删除、移动文件时，对项目文件结构进行同步修改。
/// </summary>
public class WorkFolder : IDisposable
{
    private static readonly ConcurrentDictionary<DocumentUri, WorkFolder> Folders = new();
    private readonly ConditionalWeakTable<SyntaxTree, AnalysisTree> _analysisTreeTable;
    private readonly List<SourceTextFile> _declareFiles;
    private readonly MetadataBinder _metadataBinder;
    private readonly List<SourceTextFile> _scriptFiles;

    private readonly SourceFileManager _sourceFileManager;
    private readonly WorkFolderSymbolTable _workFolderSymbolTable;
    
    private bool _changeOptionsInternal;
    private bool _isInitialized;
    private bool _needAnalysis;
    
    
    private WorkFolder(DocumentUri uri, WorkFolderOptions? options = null)
    {
        Root = DocumentUri.UriToFsPath(uri, true);
        RootUri = uri;
        Options = options ?? new WorkFolderOptions(Root);
        Options.PropertyChanged += OnSettingChanged;

        _sourceFileManager = new SourceFileManager(uri);
        _workFolderSymbolTable = new WorkFolderSymbolTable();
        _metadataBinder = new MetadataBinder(this);
        _declareFiles = [];
        _scriptFiles = [];
        _analysisTreeTable = [];

        UpdatePreDefinedMacros();

        _needAnalysis = true;
    }

    /// <summary>
    ///     获取或设置项目配置
    /// </summary>
    public WorkFolderOptions Options { get; }


    /// <summary>
    ///     当前的元数据生成文件路径。
    /// </summary>
    private string? MetadataGenFileName { get; set; }


    /// <summary>
    ///     如果当前配置忽略类型错误，将返回<see langword="true" />。
    /// </summary>
    public bool IgnoreTypeError => Options.IgnoreTypeError;


    /// <summary>
    ///     项目根目录
    /// </summary>
    public string Root { get; }


    /// <summary>
    ///     项目根目录 URI
    /// </summary>
    public DocumentUri RootUri { get; }


    public void Dispose()
    {
        _workFolderSymbolTable.Clear();
        _declareFiles.Clear();
        _scriptFiles.Clear();
        _analysisTreeTable.Clear();
        _sourceFileManager.Dispose();
        Folders.Remove(RootUri, out _);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    ///     清空已载入的项目
    /// </summary>
    public static void Clear()
    {
        Folders.Clear();
    }


    /// <summary>
    ///     更新全局类型定义文件的根路径。
    /// </summary>
    /// <param name="directory">文件夹的绝对路径</param>
    /// <exception cref="DirectoryNotFoundException"></exception>
    public static async Task SetGlobalDeclarationDirectoryAsync(string directory)
    {
        if (!Directory.Exists(directory))
        {
            throw new DirectoryNotFoundException(directory);
        }

        WorkFolder workFolder = GetWorkFolder(directory);
        await workFolder.RunAnalyzerAsync();
        GlobalSymbols.SetGlobalDeclareSymbolTable(workFolder.GetWorkFolderSymbolTable().GetDeclaredSymbols());
    }


    /// <summary>
    ///     载入新项目或获取已有项目。
    /// </summary>
    /// <param name="root">项目根路径</param>
    public static WorkFolder GetWorkFolder(string root)
    {
        // 如果给定路径是文件路径，改为文件所在的文件夹
        if (File.Exists(root))
        {
            root = Path.GetDirectoryName(root) ??
                   throw new Exception($"无效路径: '{root}'");
            // 依次向上查找文件夹，如果找到已有的项目，返回已有的即可
            string folder = root;
            DirectoryInfo? dir = new(folder);
            while (dir != null && dir.Attributes.HasFlag(FileAttributes.Directory))
            {
                DocumentUri dirUri = DocumentUriCache.GetCached(dir.FullName);
                if (Folders.TryGetValue(dirUri, out WorkFolder? existProject))
                {
                    return existProject;
                }

                dir = dir.Parent;
            }
        }

        DocumentUri uri = DocumentUriCache.GetCached(root);
        if (Folders.TryGetValue(uri, out WorkFolder? proj))
        {
            return proj;
        }

        proj = new WorkFolder(uri);
        Folders.TryAdd(uri, proj);
        return proj;
    }


    /// <summary>
    ///     获取当前已有的元数据符号定义
    /// </summary>
    public ImmutableArray<ISymbol> GetMetadataSymbols()
    {
        return _workFolderSymbolTable.ToImmutable();
    }


    internal WorkFolderSymbolTable GetWorkFolderSymbolTable()
    {
        return _workFolderSymbolTable;
    }


    private void UpdatePreDefinedMacros()
    {
        Options.PredefinedMacros.ForEach(name =>
            _workFolderSymbolTable.PreDefineMacro(MacroSymbol.PreDefine(name)));
    }


    private void OnSettingChanged(WorkFolderOptions sender, WorkFolderOptionsPropertyChangedEventArgs args)
    {
        if (!_isInitialized || _changeOptionsInternal)
        {
            return;
        }

        switch (args.PropertyName)
        {
            case nameof(WorkFolderOptions.MetadataName):
            {
                string fileName = sender.MetadataName;
                if (File.Exists(fileName))
                {
                    LoadMetadata(fileName);
                }

                break;
            }
            case nameof(WorkFolderOptions.PredefinedMacros):
            {
                UpdatePreDefinedMacros();
                break;
            }
        }
    }


    public event EventHandler<ProjectLoadingEventArgs>? LoadMetadataStarted;
    public event EventHandler<ProjectLoadingEventArgs>? LoadMetadataCompleted;
    public event EventHandler<ProjectLoadingEventArgs>? LoadProjectStarted;
    public event EventHandler<ProjectLoadingEventArgs>? LoadProjectCompleted;
    public event EventHandler? AnalyzeStarted;
    public event EventHandler? AnalyzeCompleted;


    /// <summary>
    ///     读取一个.mdd扩展名的元数据文档。
    /// </summary>
    /// <param name="fileName">文件路径</param>
    public void LoadMetadata(string fileName)
    {
        // 清空本地符号
        _workFolderSymbolTable.Clear();

        if (!string.IsNullOrEmpty(MetadataGenFileName))
        {
            _sourceFileManager.RemoveFile(MetadataGenFileName);
        }

        // 如果尚未初始化，更新 Options 的 MetadataName 属性
        _changeOptionsInternal = true;
        Options.MetadataName = fileName;
        _changeOptionsInternal = false;

        if (!_isInitialized)
        {
            return;
        }

        string dir = Path.GetDirectoryName(fileName) ?? "";
        string file = Path.GetFileNameWithoutExtension(fileName);
        MetadataGenFileName = Path.Combine(dir, $"{file}.m.dms");

        LoadMetadataStarted?.Invoke(this, new ProjectLoadingEventArgs(fileName));

        MetadataDocument mdm = new();
        mdm.Open(fileName);
        MetadataSerializer serializer = new()
        {
            Options =
            {
                Indent = 4,
                Kind = MetadataSerializerKind.Script
            }
        };

        string content = serializer.Serialize(mdm);

        if (Options.ExportMetadataSerializeResult)
        {
            File.WriteAllText(MetadataGenFileName, content);
        }

        // 重新载入元数据文件
        if (_sourceFileManager.TryCreateSourceFile(MetadataGenFileName, content, out SourceTextFile? source))
        {
            source.UpdateContent(content);
            source.Preprocessed = Preprocessor.Process(source);
            SyntaxTree tree = Parser.Parse(source);
            source.UpdateSyntaxTree(MetadataGenFileName, tree);
            _metadataBinder.Bind(tree);
        }

        LoadMetadataCompleted?.Invoke(this, new ProjectLoadingEventArgs(fileName));
    }


    private async Task Initialize()
    {
        LoadProjectStarted?.Invoke(this, new ProjectLoadingEventArgs(Root));
        await Task.Run(_sourceFileManager.Initialize);
        LoadProjectCompleted?.Invoke(this, new ProjectLoadingEventArgs(Root));
        _isInitialized = true;
        // 载入元数据文件
        if (!string.IsNullOrEmpty(Options.MetadataName) && File.Exists(Options.MetadataName))
        {
            LoadMetadata(Options.MetadataName);
        }

        // 查找入口文件并区分声明文件
        _declareFiles.Clear();
        _scriptFiles.Clear();

        _sourceFileManager.GetFiles().Where(f => f.IsEntry).ForEach(f =>
        {
            SyntaxTree tree = Parser.Parse(f);
            f.UpdateSyntaxTree(f.FileName, tree);
            if (tree.IsDeclare)
            {
                _declareFiles.Add(f);
            }
            else
            {
                _scriptFiles.Add(f);
            }
        });
    }


    /// <summary>
    ///     获取指定语法树的符号表。
    /// </summary>
    public AnalysisTree GetAnalysisTree(SyntaxTree tree)
    {
        if (_analysisTreeTable.TryGetValue(tree, out AnalysisTree? analysisTree))
        {
            return analysisTree;
        }

        analysisTree = AnalysisNodeFactory.Tree(this, tree);
        _analysisTreeTable.Add(tree, analysisTree);
        return analysisTree;
    }


    /// <summary>
    ///     更新语法树绑定的分析树
    /// </summary>
    /// <param name="tree">语法树</param>
    /// <param name="analysisTree">分析树</param>
    public void UpdateAnalysisTree(SyntaxTree tree, AnalysisTree analysisTree)
    {
        _analysisTreeTable.AddOrUpdate(tree, analysisTree);
    }


    private void Analysis()
    {
        AnalyzeStarted?.Invoke(this, EventArgs.Empty);

        // 先绑定声明文件
        _workFolderSymbolTable.GetDeclaredSymbols().Clear();
        DeclarationBinder declareBinder = new(this);
        declareBinder.Bind(_declareFiles.SelectMany(s => s.GetSyntaxTrees()));

        // 绑定后续脚本文件
        _scriptFiles.ForEach(file =>
        {
            foreach (var syntaxTree in file.GetSyntaxTrees())
            {
                Binder binder;
                if (syntaxTree.IsDms)
                {
                    binder = new DmsBinder(this);
                }
                else if (syntaxTree.IsMetadata)
                {
                    binder = new MetadataBinder(this);
                }
                else
                {
                    binder = new MrsBinder(this);
                }
                
                UpdateAnalysisTree(syntaxTree, binder.Bind(syntaxTree));
            }
        });

        _needAnalysis = false;
        AnalyzeCompleted?.Invoke(this, EventArgs.Empty);
    }


    /// <summary>
    ///     运行语法分析器
    /// </summary>
    public async Task RunAnalyzerAsync()
    {
        if (!_isInitialized)
        {
            await Initialize();
            Analysis();
        }

        if (!_needAnalysis)
        {
            return;
        }

        Analysis();
    }


    /// <summary>
    ///     获取已读取的文件列表。
    /// </summary>
    public ImmutableArray<SourceTextFile> GetSourceFiles()
    {
        return [.._sourceFileManager.GetFiles()];
    }


    /// <summary>
    ///     查找指定URI的文件，如果不存在，返回<see langword="null" />。
    /// </summary>
    public SourceTextFile? FindFile(DocumentUri uri)
    {
        return _sourceFileManager.TryGetFileByPath(uri, out SourceTextFile? src) ? src : null;
    }


    /// <summary>
    ///     查找指定路径的文件，可以是绝对路径或相对于根路径的相对路径。
    ///     如果不存在，返回<see langword="null" />。
    /// </summary>
    public SourceTextFile? FindFile(string path)
    {
        if (!Path.IsPathRooted(path))
        {
            path = Path.GetFullPath(path, Root);
        }

        return FindFile(DocumentUriCache.GetCached(path));
    }
}


public class ProjectLoadingEventArgs(string path)
{
    public string Path { get; } = path;
}